package compile

type token = int
type operator = int
type numType = int
type numBase = int

type Token struct {
	value   string
	kind    token
	op      operator
	numType numType
	numBase numBase
	pos     Pos /* a pos copy */
}

const (
	_EOF = iota
	_INVALID
	_COMMENT

	_OPERATOR

	_LPAREN
	_LBRACK
	_LBRACE

	_RPAREN
	_RBRACK
	_RBRACE

	_COMMA
	_SEMI
	_DOT
	_DDD
	_COLON

	_LARROW
	_RARROW
	_STAR

	_IDENTIFIER
	_NUMBER
	_STRING
	_CHAR

	_ELSE
	_ELIF
	_IF
	_PACKAGE
	_IMPORT
	_CLASS
	_INTERFACE
	_VAR
	_NEW
	_FUNC
	_PUBLIC
	_PROTECTED
	_PRIVATE
	_STATIC
	_CONST
	_RETURN
	_TRY
	_CATCH
	_FINALLY
	_WHILE
	_AS
	_BREAK
	_CONTINUE
	_SWITCH
	_CASE
	_VOID
	_OPERATOR_OVL
	_ENUM
	_OVERRIDE
	_DEFAULT
	_THEN
	_THROW
	_ASSERT
	_NATIVE
	_GOTO
	_FOR
)

var keywordStart = _IF
var keywordEnd = _FOR

// Operators

const (
	_MUIT = iota
	_DIVI
	_MOD
	_XOR
	_BAND
	_BOR
	_LSHR
	_RSHR

	_EQ
	_UEQ
	_GTH
	_LTH
	_GEQ
	_LEQ

	_OR
	_AND

	_PLUSPLUS
	_SUBSUB
	_PLUS
	_SUB
	_BNG // ~
	_NOT

	_ASSIGN
	_ASSIGN_OP

	_OP_SUBSCR // for op overload
)

// Number
const (
	_FLOAT   = 102 // 0.8f
	_DOUBLE  = 100 // 0.8
	_INTEGER = 105 // 3
	_LONG    = 108 // 50l
)

// Number bases
const (
	_ORD = 10 // 726
	_HEX = 16 // 0xff
	_OCT = 8  // 0o555
	_BIN = 2  // 0b0101
)

func getTokenName(tok token) string {
	if tok >= 0 && tok < len(tokenNames) {
		return tokenNames[tok]
	}
	return "<INVALID>"
}

var tokenNames []string = []string{
	"_EOF",
	"_INVALID",
	"_COMMENT",

	"_OPERATOR",

	"_LPAREN",
	"_LBRACK",
	"_LBRACE",

	"_RPAREN",
	"_RBRACK",
	"_RBRACE",

	"_COMMA",
	"_SEMI",
	"_DOT",
	"_DDD",
	"_COLON",

	"_LARROW",
	"_RARROW",
	"_STAR",

	"_IDENTIFIER",
	"_NUMBER",
	"_STRING",
	"_CHAR",

	"_ELSE",
	"_ELIF",
	"_IF",
	"_PACKAGE",
	"_IMPORT",
	"_CLASS",
	"_INTERFACE",
	"_VAR",
	"_NEW",
	"_FUNC",
	"_PUBLIC",
	"_PROTECTED",
	"_PRIVATE",
	"_STATIC",
	"_CONST",
	"_RETURN",
	"_TRY",
	"_CATCH",
	"_FINALLY",
	"_WHILE",
	"_AS",
	"_BREAK",
	"_CONTINUE",
	"_SWITCH",
	"_CASE",
	"_VOID",
	"_OPERATOR_OVL",
	"_ENUM",
	"_OVERRIDE",
	"_DEFAULT",
	"_THEN",
	"_THROW",
	"_ASSERT",
	"_NATIVE",
	"_GOTO",
	"_FOR",
}

func getOperatorName(op operator) string {
	if op >= 0 && op < len(operatorNames) {
		return operatorNames[op]
	}
	return "<INVALID>"
}

var operatorNames []string = []string{
	"_MUIT",
	"_DIVI",
	"_MOD",
	"_XOR",
	"_BAND",
	"_BOR",
	"_LSHR",
	"_RSHR",

	"_EQ",
	"_UEQ",
	"_GTH",
	"_LTH",
	"_GEQ",
	"_LEQ",

	"_OR",
	"_AND",

	"_PLUSPLUS",
	"_SUBSUB",
	"_PLUS",
	"_SUB",
	"_BNG",
	"_NOT",

	"_ASSIGN",
	"_ASSIGN_OP",
}

var keywordMap map[string]token = map[string]token{
	"if":        _IF,
	"else":      _ELSE,
	"elif":      _ELIF,
	"package":   _PACKAGE,
	"import":    _IMPORT,
	"class":     _CLASS,
	"interface": _INTERFACE,
	"var":       _VAR,
	"new":       _NEW,
	"func":      _FUNC,
	"public":    _PUBLIC,
	"protected": _PROTECTED,
	"private":   _PRIVATE,
	"static":    _STATIC,
	"const":     _CONST,
	"return":    _RETURN,
	"try":       _TRY,
	"catch":     _CATCH,
	"finally":   _FINALLY,
	"while":     _WHILE,
	"for":       _FOR,
	"as":        _AS,
	"not":       _NOT,
	"and":       _AND,
	"or":        _OR,
	"break":     _BREAK,
	"continue":  _CONTINUE,
	"switch":    _SWITCH,
	"case":      _CASE,
	"void":      _VOID,
	"enum":      _ENUM,
	"operator":  _OPERATOR_OVL,
	"override":  _OVERRIDE,
	"default":   _DEFAULT,
	"then":      _THEN,
	"throw":     _THROW,
	"assert":    _ASSERT,
	"native":    _NATIVE,
	"goto":      _GOTO,
}

func isKeyword(tokv string) bool {
	_, ok := keywordMap[tokv]

	return ok
}

var opPrecMap map[int]int = map[int]int{
	_ASSIGN:    10,
	_ASSIGN_OP: 10,
	_OR:        30,
	_AND:       40,
	_BOR:       50,
	_XOR:       60,
	_BAND:      70,
	_EQ:        80,
	_UEQ:       80,
	_LTH:       90,
	_LEQ:       90,
	_GTH:       90,
	_GEQ:       90,
	_LSHR:      100,
	_RSHR:      100,
	_PLUS:      110,
	_SUB:       110,
	_MUIT:      120,
	_DIVI:      130,
	_MOD:       140,
}

func getOpPrec(op operator, tok token) int {
	find := -1

	switch tok {

	case _OPERATOR:
		find = op

	default:
		return -1

	}

	v, ok := opPrecMap[find]
	if !ok {
		return -1
	}

	return v
}

var baseType []string = []string{
	"int8", "int16", "int", "int64",
	"float32", "float64",
	"uint8", "uint16", "uint", "uint64",
	"byte", "short", "char", "long",
	"double", "float", "bool",
}

var _baseTypeAlias map[string]string = map[string]string{
	"float":  "float32",
	"double": "float64",
	"byte":   "int8",
	"short":  "int16",
	"char":   "uint16",
	"long":   "int64",
}

func checkBeasTypeAlias(vo string) string {
	if v, ok := _baseTypeAlias[vo]; ok {
		return v
	}
	return vo
}

func isBaseType(tv string) bool {
	for _, v := range baseType {
		if tv == v {
			return true
		}
	}
	return false
}

var validOverloadOp []operator = []operator{
	_PLUS,
	_SUB,
	_MUIT,
	_DIVI,
	_MOD,
	_EQ,
	_UEQ,
	_XOR,
	_BAND,
	_BOR,
	_OP_SUBSCR,
	_LSHR,
	_RSHR,
}

var validOverloadOpNames map[string]operator = map[string]operator{
	"add":    _PLUS,
	"sub":    _SUB,
	"muit":   _MUIT,
	"div":    _DIVI,
	"mod":    _MOD,
	"eq":     _EQ,
	"ueq":    _UEQ,
	"xor":    _XOR,
	"and":    _BAND,
	"or":     _BOR,
	"subsur": _OP_SUBSCR,
	"lshr":   _LSHR,
	"rshr":   _RSHR,
}

func isValidOverloadOp(op operator) bool {
	for _, v := range validOverloadOp {
		if v == op {
			return true
		}
	}
	return false
}
